{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/ben/miniconda3/envs/main/lib/python3.9/site-packages/thinc/compat.py:36: UserWarning: 'has_mps' is deprecated, please use 'torch.backends.mps.is_built()'\n",
      "  hasattr(torch, \"has_mps\")\n",
      "/home/ben/miniconda3/envs/main/lib/python3.9/site-packages/thinc/compat.py:37: UserWarning: 'has_mps' is deprecated, please use 'torch.backends.mps.is_built()'\n",
      "  and torch.has_mps  # type: ignore[attr-defined]\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "import random\n",
    "import numpy as np\n",
    "import spacy\n",
    "import datasets\n",
    "import torchtext\n",
    "import tqdm\n",
    "import evaluate\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.ticker as ticker"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "seed = 1234\n",
    "\n",
    "random.seed(seed)\n",
    "np.random.seed(seed)\n",
    "torch.manual_seed(seed)\n",
    "torch.cuda.manual_seed(seed)\n",
    "torch.backends.cudnn.deterministic = True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset = datasets.load_dataset(\"bentrevett/multi30k\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_data, valid_data, test_data = (\n",
    "    dataset[\"train\"],\n",
    "    dataset[\"validation\"],\n",
    "    dataset[\"test\"],\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/ben/miniconda3/envs/main/lib/python3.9/site-packages/spacy/util.py:910: UserWarning: [W095] Model 'en_core_web_sm' (3.5.0) was trained with spaCy v3.5.0 and may not be 100% compatible with the current version (3.7.2). If you see errors or degraded performance, download a newer compatible model or retrain your custom model with the current spaCy version. For more details and available updates, run: python -m spacy validate\n",
      "  warnings.warn(warn_msg)\n",
      "/home/ben/miniconda3/envs/main/lib/python3.9/site-packages/spacy/util.py:910: UserWarning: [W095] Model 'de_core_news_sm' (3.5.0) was trained with spaCy v3.5.0 and may not be 100% compatible with the current version (3.7.2). If you see errors or degraded performance, download a newer compatible model or retrain your custom model with the current spaCy version. For more details and available updates, run: python -m spacy validate\n",
      "  warnings.warn(warn_msg)\n"
     ]
    }
   ],
   "source": [
    "en_nlp = spacy.load(\"en_core_web_sm\")\n",
    "de_nlp = spacy.load(\"de_core_news_sm\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def tokenize_example(example, en_nlp, de_nlp, max_length, lower, sos_token, eos_token):\n",
    "    en_tokens = [token.text for token in en_nlp.tokenizer(example[\"en\"])][:max_length]\n",
    "    de_tokens = [token.text for token in de_nlp.tokenizer(example[\"de\"])][:max_length]\n",
    "    if lower:\n",
    "        en_tokens = [token.lower() for token in en_tokens]\n",
    "        de_tokens = [token.lower() for token in de_tokens]\n",
    "    en_tokens = [sos_token] + en_tokens + [eos_token]\n",
    "    de_tokens = [sos_token] + de_tokens + [eos_token]\n",
    "    return {\"en_tokens\": en_tokens, \"de_tokens\": de_tokens}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a0c9d2e9cbb44c6c84f3e132b077ea9a",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Map:   0%|          | 0/1014 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "d1869cef9cec4aaaac7e7b94687aaa67",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Map:   0%|          | 0/1000 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "max_length = 1_000\n",
    "lower = True\n",
    "sos_token = \"<sos>\"\n",
    "eos_token = \"<eos>\"\n",
    "\n",
    "fn_kwargs = {\n",
    "    \"en_nlp\": en_nlp,\n",
    "    \"de_nlp\": de_nlp,\n",
    "    \"max_length\": max_length,\n",
    "    \"lower\": lower,\n",
    "    \"sos_token\": sos_token,\n",
    "    \"eos_token\": eos_token,\n",
    "}\n",
    "\n",
    "train_data = train_data.map(tokenize_example, fn_kwargs=fn_kwargs)\n",
    "valid_data = valid_data.map(tokenize_example, fn_kwargs=fn_kwargs)\n",
    "test_data = test_data.map(tokenize_example, fn_kwargs=fn_kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "min_freq = 2\n",
    "unk_token = \"<unk>\"\n",
    "pad_token = \"<pad>\"\n",
    "\n",
    "special_tokens = [\n",
    "    unk_token,\n",
    "    pad_token,\n",
    "    sos_token,\n",
    "    eos_token,\n",
    "]\n",
    "\n",
    "en_vocab = torchtext.vocab.build_vocab_from_iterator(\n",
    "    train_data[\"en_tokens\"],\n",
    "    min_freq=min_freq,\n",
    "    specials=special_tokens,\n",
    ")\n",
    "\n",
    "de_vocab = torchtext.vocab.build_vocab_from_iterator(\n",
    "    train_data[\"de_tokens\"],\n",
    "    min_freq=min_freq,\n",
    "    specials=special_tokens,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert en_vocab[unk_token] == de_vocab[unk_token]\n",
    "assert en_vocab[pad_token] == de_vocab[pad_token]\n",
    "\n",
    "unk_index = en_vocab[unk_token]\n",
    "pad_index = en_vocab[pad_token]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "en_vocab.set_default_index(unk_index)\n",
    "de_vocab.set_default_index(unk_index)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def numericalize_example(example, en_vocab, de_vocab):\n",
    "    en_ids = en_vocab.lookup_indices(example[\"en_tokens\"])\n",
    "    de_ids = de_vocab.lookup_indices(example[\"de_tokens\"])\n",
    "    return {\"en_ids\": en_ids, \"de_ids\": de_ids}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f815aaa8443d42209cf6497558b81f48",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Map:   0%|          | 0/1014 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a4e052deb3524650bfdd2f5dc17d79d7",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Map:   0%|          | 0/1000 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fn_kwargs = {\"en_vocab\": en_vocab, \"de_vocab\": de_vocab}\n",
    "\n",
    "train_data = train_data.map(numericalize_example, fn_kwargs=fn_kwargs)\n",
    "valid_data = valid_data.map(numericalize_example, fn_kwargs=fn_kwargs)\n",
    "test_data = test_data.map(numericalize_example, fn_kwargs=fn_kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_type = \"torch\"\n",
    "format_columns = [\"en_ids\", \"de_ids\"]\n",
    "\n",
    "train_data = train_data.with_format(\n",
    "    type=data_type, columns=format_columns, output_all_columns=True\n",
    ")\n",
    "\n",
    "valid_data = valid_data.with_format(\n",
    "    type=data_type,\n",
    "    columns=format_columns,\n",
    "    output_all_columns=True,\n",
    ")\n",
    "\n",
    "test_data = test_data.with_format(\n",
    "    type=data_type,\n",
    "    columns=format_columns,\n",
    "    output_all_columns=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_collate_fn(pad_index):\n",
    "    def collate_fn(batch):\n",
    "        batch_en_ids = [example[\"en_ids\"] for example in batch]\n",
    "        batch_de_ids = [example[\"de_ids\"] for example in batch]\n",
    "        batch_en_ids = nn.utils.rnn.pad_sequence(batch_en_ids, padding_value=pad_index)\n",
    "        batch_de_ids = nn.utils.rnn.pad_sequence(batch_de_ids, padding_value=pad_index)\n",
    "        batch = {\n",
    "            \"en_ids\": batch_en_ids,\n",
    "            \"de_ids\": batch_de_ids,\n",
    "        }\n",
    "        return batch\n",
    "\n",
    "    return collate_fn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_data_loader(dataset, batch_size, pad_index, shuffle=False):\n",
    "    collate_fn = get_collate_fn(pad_index)\n",
    "    data_loader = torch.utils.data.DataLoader(\n",
    "        dataset=dataset,\n",
    "        batch_size=batch_size,\n",
    "        collate_fn=collate_fn,\n",
    "        shuffle=shuffle,\n",
    "    )\n",
    "    return data_loader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "batch_size = 128\n",
    "\n",
    "train_data_loader = get_data_loader(train_data, batch_size, pad_index, shuffle=True)\n",
    "valid_data_loader = get_data_loader(valid_data, batch_size, pad_index)\n",
    "test_data_loader = get_data_loader(test_data, batch_size, pad_index)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Encoder(nn.Module):\n",
    "    def __init__(\n",
    "        self, input_dim, embedding_dim, encoder_hidden_dim, decoder_hidden_dim, dropout\n",
    "    ):\n",
    "        super().__init__()\n",
    "        self.embedding = nn.Embedding(input_dim, embedding_dim)\n",
    "        self.rnn = nn.GRU(embedding_dim, encoder_hidden_dim, bidirectional=True)\n",
    "        self.fc = nn.Linear(encoder_hidden_dim * 2, decoder_hidden_dim)\n",
    "        self.dropout = nn.Dropout(dropout)\n",
    "\n",
    "    def forward(self, src):\n",
    "        # src = [src length, batch size]\n",
    "        embedded = self.dropout(self.embedding(src))\n",
    "        # embedded = [src length, batch size, embedding dim]\n",
    "        outputs, hidden = self.rnn(embedded)\n",
    "        # outputs = [src length, batch size, hidden dim * n directions]\n",
    "        # hidden = [n layers * n directions, batch size, hidden dim]\n",
    "        # hidden is stacked [forward_1, backward_1, forward_2, backward_2, ...]\n",
    "        # outputs are always from the last layer\n",
    "        # hidden [-2, :, : ] is the last of the forwards RNN\n",
    "        # hidden [-1, :, : ] is the last of the backwards RNN\n",
    "        # initial decoder hidden is final hidden state of the forwards and backwards\n",
    "        # encoder RNNs fed through a linear layer\n",
    "        hidden = torch.tanh(\n",
    "            self.fc(torch.cat((hidden[-2, :, :], hidden[-1, :, :]), dim=1))\n",
    "        )\n",
    "        # outputs = [src length, batch size, encoder hidden dim * 2]\n",
    "        # hidden = [batch size, decoder hidden dim]\n",
    "        return outputs, hidden"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Attention(nn.Module):\n",
    "    def __init__(self, encoder_hidden_dim, decoder_hidden_dim):\n",
    "        super().__init__()\n",
    "        self.attn_fc = nn.Linear(\n",
    "            (encoder_hidden_dim * 2) + decoder_hidden_dim, decoder_hidden_dim\n",
    "        )\n",
    "        self.v_fc = nn.Linear(decoder_hidden_dim, 1, bias=False)\n",
    "\n",
    "    def forward(self, hidden, encoder_outputs):\n",
    "        # hidden = [batch size, decoder hidden dim]\n",
    "        # encoder_outputs = [src length, batch size, encoder hidden dim * 2]\n",
    "        batch_size = encoder_outputs.shape[1]\n",
    "        src_length = encoder_outputs.shape[0]\n",
    "        # repeat decoder hidden state src_length times\n",
    "        hidden = hidden.unsqueeze(1).repeat(1, src_length, 1)\n",
    "        encoder_outputs = encoder_outputs.permute(1, 0, 2)\n",
    "        # hidden = [batch size, src length, decoder hidden dim]\n",
    "        # encoder_outputs = [batch size, src length, encoder hidden dim * 2]\n",
    "        energy = torch.tanh(self.attn_fc(torch.cat((hidden, encoder_outputs), dim=2)))\n",
    "        # energy = [batch size, src length, decoder hidden dim]\n",
    "        attention = self.v_fc(energy).squeeze(2)\n",
    "        # attention = [batch size, src length]\n",
    "        return torch.softmax(attention, dim=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Decoder(nn.Module):\n",
    "    def __init__(\n",
    "        self,\n",
    "        output_dim,\n",
    "        embedding_dim,\n",
    "        encoder_hidden_dim,\n",
    "        decoder_hidden_dim,\n",
    "        dropout,\n",
    "        attention,\n",
    "    ):\n",
    "        super().__init__()\n",
    "        self.output_dim = output_dim\n",
    "        self.attention = attention\n",
    "        self.embedding = nn.Embedding(output_dim, embedding_dim)\n",
    "        self.rnn = nn.GRU((encoder_hidden_dim * 2) + embedding_dim, decoder_hidden_dim)\n",
    "        self.fc_out = nn.Linear(\n",
    "            (encoder_hidden_dim * 2) + decoder_hidden_dim + embedding_dim, output_dim\n",
    "        )\n",
    "        self.dropout = nn.Dropout(dropout)\n",
    "\n",
    "    def forward(self, input, hidden, encoder_outputs):\n",
    "        # input = [batch size]\n",
    "        # hidden = [batch size, decoder hidden dim]\n",
    "        # encoder_outputs = [src length, batch size, encoder hidden dim * 2]\n",
    "        input = input.unsqueeze(0)\n",
    "        # input = [1, batch size]\n",
    "        embedded = self.dropout(self.embedding(input))\n",
    "        # embedded = [1, batch size, embedding dim]\n",
    "        a = self.attention(hidden, encoder_outputs)\n",
    "        # a = [batch size, src length]\n",
    "        a = a.unsqueeze(1)\n",
    "        # a = [batch size, 1, src length]\n",
    "        encoder_outputs = encoder_outputs.permute(1, 0, 2)\n",
    "        # encoder_outputs = [batch size, src length, encoder hidden dim * 2]\n",
    "        weighted = torch.bmm(a, encoder_outputs)\n",
    "        # weighted = [batch size, 1, encoder hidden dim * 2]\n",
    "        weighted = weighted.permute(1, 0, 2)\n",
    "        # weighted = [1, batch size, encoder hidden dim * 2]\n",
    "        rnn_input = torch.cat((embedded, weighted), dim=2)\n",
    "        # rnn_input = [1, batch size, (encoder hidden dim * 2) + embedding dim]\n",
    "        output, hidden = self.rnn(rnn_input, hidden.unsqueeze(0))\n",
    "        # output = [seq length, batch size, decoder hid dim * n directions]\n",
    "        # hidden = [n layers * n directions, batch size, decoder hid dim]\n",
    "        # seq len, n layers and n directions will always be 1 in this decoder, therefore:\n",
    "        # output = [1, batch size, decoder hidden dim]\n",
    "        # hidden = [1, batch size, decoder hidden dim]\n",
    "        # this also means that output == hidden\n",
    "        assert (output == hidden).all()\n",
    "        embedded = embedded.squeeze(0)\n",
    "        output = output.squeeze(0)\n",
    "        weighted = weighted.squeeze(0)\n",
    "        prediction = self.fc_out(torch.cat((output, weighted, embedded), dim=1))\n",
    "        # prediction = [batch size, output dim]\n",
    "        return prediction, hidden.squeeze(0), a.squeeze(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Seq2Seq(nn.Module):\n",
    "    def __init__(self, encoder, decoder, device):\n",
    "        super().__init__()\n",
    "        self.encoder = encoder\n",
    "        self.decoder = decoder\n",
    "        self.device = device\n",
    "\n",
    "    def forward(self, src, trg, teacher_forcing_ratio):\n",
    "        # src = [src length, batch size]\n",
    "        # trg = [trg length, batch size]\n",
    "        # teacher_forcing_ratio is probability to use teacher forcing\n",
    "        # e.g. if teacher_forcing_ratio is 0.75 we use teacher forcing 75% of the time\n",
    "        batch_size = src.shape[1]\n",
    "        trg_length = trg.shape[0]\n",
    "        trg_vocab_size = self.decoder.output_dim\n",
    "        # tensor to store decoder outputs\n",
    "        outputs = torch.zeros(trg_length, batch_size, trg_vocab_size).to(self.device)\n",
    "        # encoder_outputs is all hidden states of the input sequence, back and forwards\n",
    "        # hidden is the final forward and backward hidden states, passed through a linear layer\n",
    "        encoder_outputs, hidden = self.encoder(src)\n",
    "        # outputs = [src length, batch size, encoder hidden dim * 2]\n",
    "        # hidden = [batch size, decoder hidden dim]\n",
    "        # first input to the decoder is the <sos> tokens\n",
    "        input = trg[0, :]\n",
    "        for t in range(1, trg_length):\n",
    "            # insert input token embedding, previous hidden state and all encoder hidden states\n",
    "            # receive output tensor (predictions) and new hidden state\n",
    "            output, hidden, _ = self.decoder(input, hidden, encoder_outputs)\n",
    "            # output = [batch size, output dim]\n",
    "            # hidden = [n layers, batch size, decoder hidden dim]\n",
    "            # place predictions in a tensor holding predictions for each token\n",
    "            outputs[t] = output\n",
    "            # decide if we are going to use teacher forcing or not\n",
    "            teacher_force = random.random() < teacher_forcing_ratio\n",
    "            # get the highest predicted token from our predictions\n",
    "            top1 = output.argmax(1)\n",
    "            # if teacher forcing, use actual next token as next input\n",
    "            # if not, use predicted token\n",
    "            input = trg[t] if teacher_force else top1\n",
    "            # input = [batch size]\n",
    "        return outputs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "input_dim = len(de_vocab)\n",
    "output_dim = len(en_vocab)\n",
    "encoder_embedding_dim = 256\n",
    "decoder_embedding_dim = 256\n",
    "encoder_hidden_dim = 512\n",
    "decoder_hidden_dim = 512\n",
    "encoder_dropout = 0.5\n",
    "decoder_dropout = 0.5\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "\n",
    "attention = Attention(encoder_hidden_dim, decoder_hidden_dim)\n",
    "\n",
    "encoder = Encoder(\n",
    "    input_dim,\n",
    "    encoder_embedding_dim,\n",
    "    encoder_hidden_dim,\n",
    "    decoder_hidden_dim,\n",
    "    encoder_dropout,\n",
    ")\n",
    "\n",
    "decoder = Decoder(\n",
    "    output_dim,\n",
    "    decoder_embedding_dim,\n",
    "    encoder_hidden_dim,\n",
    "    decoder_hidden_dim,\n",
    "    decoder_dropout,\n",
    "    attention,\n",
    ")\n",
    "\n",
    "model = Seq2Seq(encoder, decoder, device).to(device)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Seq2Seq(\n",
       "  (encoder): Encoder(\n",
       "    (embedding): Embedding(7853, 256)\n",
       "    (rnn): GRU(256, 512, bidirectional=True)\n",
       "    (fc): Linear(in_features=1024, out_features=512, bias=True)\n",
       "    (dropout): Dropout(p=0.5, inplace=False)\n",
       "  )\n",
       "  (decoder): Decoder(\n",
       "    (attention): Attention(\n",
       "      (attn_fc): Linear(in_features=1536, out_features=512, bias=True)\n",
       "      (v_fc): Linear(in_features=512, out_features=1, bias=False)\n",
       "    )\n",
       "    (embedding): Embedding(5893, 256)\n",
       "    (rnn): GRU(1280, 512)\n",
       "    (fc_out): Linear(in_features=1792, out_features=5893, bias=True)\n",
       "    (dropout): Dropout(p=0.5, inplace=False)\n",
       "  )\n",
       ")"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def init_weights(m):\n",
    "    for name, param in m.named_parameters():\n",
    "        if \"weight\" in name:\n",
    "            nn.init.normal_(param.data, mean=0, std=0.01)\n",
    "        else:\n",
    "            nn.init.constant_(param.data, 0)\n",
    "\n",
    "\n",
    "model.apply(init_weights)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The model has 20,518,405 trainable parameters\n"
     ]
    }
   ],
   "source": [
    "def count_parameters(model):\n",
    "    return sum(p.numel() for p in model.parameters() if p.requires_grad)\n",
    "\n",
    "\n",
    "print(f\"The model has {count_parameters(model):,} trainable parameters\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer = optim.Adam(model.parameters())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "criterion = nn.CrossEntropyLoss(ignore_index=pad_index)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_fn(\n",
    "    model, data_loader, optimizer, criterion, clip, teacher_forcing_ratio, device\n",
    "):\n",
    "    model.train()\n",
    "    epoch_loss = 0\n",
    "    for i, batch in enumerate(data_loader):\n",
    "        src = batch[\"de_ids\"].to(device)\n",
    "        trg = batch[\"en_ids\"].to(device)\n",
    "        # src = [src length, batch size]\n",
    "        # trg = [trg length, batch size]\n",
    "        optimizer.zero_grad()\n",
    "        output = model(src, trg, teacher_forcing_ratio)\n",
    "        # output = [trg length, batch size, trg vocab size]\n",
    "        output_dim = output.shape[-1]\n",
    "        output = output[1:].view(-1, output_dim)\n",
    "        # output = [(trg length - 1) * batch size, trg vocab size]\n",
    "        trg = trg[1:].view(-1)\n",
    "        # trg = [(trg length - 1) * batch size]\n",
    "        loss = criterion(output, trg)\n",
    "        loss.backward()\n",
    "        torch.nn.utils.clip_grad_norm_(model.parameters(), clip)\n",
    "        optimizer.step()\n",
    "        epoch_loss += loss.item()\n",
    "    return epoch_loss / len(data_loader)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate_fn(model, data_loader, criterion, device):\n",
    "    model.eval()\n",
    "    epoch_loss = 0\n",
    "    with torch.no_grad():\n",
    "        for i, batch in enumerate(data_loader):\n",
    "            src = batch[\"de_ids\"].to(device)\n",
    "            trg = batch[\"en_ids\"].to(device)\n",
    "            # src = [src length, batch size]\n",
    "            # trg = [trg length, batch size]\n",
    "            output = model(src, trg, 0)  # turn off teacher forcing\n",
    "            # output = [trg length, batch size, trg vocab size]\n",
    "            output_dim = output.shape[-1]\n",
    "            output = output[1:].view(-1, output_dim)\n",
    "            # output = [(trg length - 1) * batch size, trg vocab size]\n",
    "            trg = trg[1:].view(-1)\n",
    "            # trg = [(trg length - 1) * batch size]\n",
    "            loss = criterion(output, trg)\n",
    "            epoch_loss += loss.item()\n",
    "    return epoch_loss / len(data_loader)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 10%|██████████████▋                                                                                                                                    | 1/10 [00:29<04:26, 29.61s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   5.011 | Train PPL: 150.091\n",
      "\tValid Loss:   4.800 | Valid PPL: 121.483\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 20%|█████████████████████████████▍                                                                                                                     | 2/10 [00:58<03:52, 29.11s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   4.159 | Train PPL:  64.018\n",
      "\tValid Loss:   4.340 | Valid PPL:  76.672\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 30%|████████████████████████████████████████████                                                                                                       | 3/10 [01:27<03:24, 29.18s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   3.461 | Train PPL:  31.851\n",
      "\tValid Loss:   3.727 | Valid PPL:  41.573\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 40%|██████████████████████████████████████████████████████████▊                                                                                        | 4/10 [01:56<02:55, 29.25s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   2.912 | Train PPL:  18.399\n",
      "\tValid Loss:   3.427 | Valid PPL:  30.789\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 50%|█████████████████████████████████████████████████████████████████████████▌                                                                         | 5/10 [02:25<02:25, 29.10s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   2.520 | Train PPL:  12.433\n",
      "\tValid Loss:   3.326 | Valid PPL:  27.837\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 60%|████████████████████████████████████████████████████████████████████████████████████████▏                                                          | 6/10 [02:54<01:56, 29.04s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   2.216 | Train PPL:   9.171\n",
      "\tValid Loss:   3.261 | Valid PPL:  26.086\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 70%|██████████████████████████████████████████████████████████████████████████████████████████████████████▉                                            | 7/10 [03:23<01:26, 28.87s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   1.989 | Train PPL:   7.305\n",
      "\tValid Loss:   3.341 | Valid PPL:  28.245\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 80%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▌                             | 8/10 [03:52<00:57, 28.92s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   1.764 | Train PPL:   5.837\n",
      "\tValid Loss:   3.328 | Valid PPL:  27.880\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 90%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎              | 9/10 [04:21<00:28, 28.87s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   1.622 | Train PPL:   5.065\n",
      "\tValid Loss:   3.376 | Valid PPL:  29.241\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 10/10 [04:50<00:00, 29.05s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tTrain Loss:   1.478 | Train PPL:   4.383\n",
      "\tValid Loss:   3.418 | Valid PPL:  30.512\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "n_epochs = 10\n",
    "clip = 1.0\n",
    "teacher_forcing_ratio = 0.5\n",
    "\n",
    "best_valid_loss = float(\"inf\")\n",
    "\n",
    "for epoch in tqdm.tqdm(range(n_epochs)):\n",
    "    train_loss = train_fn(\n",
    "        model,\n",
    "        train_data_loader,\n",
    "        optimizer,\n",
    "        criterion,\n",
    "        clip,\n",
    "        teacher_forcing_ratio,\n",
    "        device,\n",
    "    )\n",
    "    valid_loss = evaluate_fn(\n",
    "        model,\n",
    "        valid_data_loader,\n",
    "        criterion,\n",
    "        device,\n",
    "    )\n",
    "    if valid_loss < best_valid_loss:\n",
    "        best_valid_loss = valid_loss\n",
    "        torch.save(model.state_dict(), \"tut3-model.pt\")\n",
    "    print(f\"\\tTrain Loss: {train_loss:7.3f} | Train PPL: {np.exp(train_loss):7.3f}\")\n",
    "    print(f\"\\tValid Loss: {valid_loss:7.3f} | Valid PPL: {np.exp(valid_loss):7.3f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "| Test Loss: 3.242 | Test PPL:  25.572 |\n"
     ]
    }
   ],
   "source": [
    "model.load_state_dict(torch.load(\"tut3-model.pt\"))\n",
    "\n",
    "test_loss = evaluate_fn(model, test_data_loader, criterion, device)\n",
    "\n",
    "print(f\"| Test Loss: {test_loss:.3f} | Test PPL: {np.exp(test_loss):7.3f} |\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "def translate_sentence(\n",
    "    sentence,\n",
    "    model,\n",
    "    en_nlp,\n",
    "    de_nlp,\n",
    "    en_vocab,\n",
    "    de_vocab,\n",
    "    lower,\n",
    "    sos_token,\n",
    "    eos_token,\n",
    "    device,\n",
    "    max_output_length=25,\n",
    "):\n",
    "    model.eval()\n",
    "    with torch.no_grad():\n",
    "        if isinstance(sentence, str):\n",
    "            de_tokens = [token.text for token in de_nlp.tokenizer(sentence)]\n",
    "        else:\n",
    "            de_tokens = [token for token in sentence]\n",
    "        if lower:\n",
    "            de_tokens = [token.lower() for token in de_tokens]\n",
    "        de_tokens = [sos_token] + de_tokens + [eos_token]\n",
    "        ids = de_vocab.lookup_indices(de_tokens)\n",
    "        tensor = torch.LongTensor(ids).unsqueeze(-1).to(device)\n",
    "        encoder_outputs, hidden = model.encoder(tensor)\n",
    "        inputs = en_vocab.lookup_indices([sos_token])\n",
    "        attentions = torch.zeros(max_output_length, 1, len(ids))\n",
    "        for i in range(max_output_length):\n",
    "            inputs_tensor = torch.LongTensor([inputs[-1]]).to(device)\n",
    "            output, hidden, attention = model.decoder(\n",
    "                inputs_tensor, hidden, encoder_outputs\n",
    "            )\n",
    "            attentions[i] = attention\n",
    "            predicted_token = output.argmax(-1).item()\n",
    "            inputs.append(predicted_token)\n",
    "            if predicted_token == en_vocab[eos_token]:\n",
    "                break\n",
    "        en_tokens = en_vocab.lookup_tokens(inputs)\n",
    "    return en_tokens, de_tokens, attentions[: len(en_tokens) - 1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_attention(sentence, translation, attention):\n",
    "    fig, ax = plt.subplots(figsize=(10, 10))\n",
    "    attention = attention.squeeze(1).numpy()\n",
    "    cax = ax.matshow(attention, cmap=\"bone\")\n",
    "    ax.set_xticks(ticks=np.arange(len(sentence)), labels=sentence, rotation=90, size=15)\n",
    "    translation = translation[1:]\n",
    "    ax.set_yticks(ticks=np.arange(len(translation)), labels=translation, size=15)\n",
    "    plt.show()\n",
    "    plt.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('Ein Mann mit einem orangefarbenen Hut, der etwas anstarrt.',\n",
       " 'A man in an orange hat starring at something.')"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sentence = test_data[0][\"de\"]\n",
    "expected_translation = test_data[0][\"en\"]\n",
    "\n",
    "sentence, expected_translation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "translation, sentence_tokens, attention = translate_sentence(\n",
    "    sentence,\n",
    "    model,\n",
    "    en_nlp,\n",
    "    de_nlp,\n",
    "    en_vocab,\n",
    "    de_vocab,\n",
    "    lower,\n",
    "    sos_token,\n",
    "    eos_token,\n",
    "    device,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['<sos>',\n",
       " 'a',\n",
       " 'man',\n",
       " 'with',\n",
       " 'an',\n",
       " 'orange',\n",
       " 'hat',\n",
       " 'is',\n",
       " 'welding',\n",
       " 'something',\n",
       " '.',\n",
       " '<eos>']"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "translation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['<sos>',\n",
       " 'ein',\n",
       " 'mann',\n",
       " 'mit',\n",
       " 'einem',\n",
       " 'orangefarbenen',\n",
       " 'hut',\n",
       " ',',\n",
       " 'der',\n",
       " 'etwas',\n",
       " 'anstarrt',\n",
       " '.',\n",
       " '<eos>']"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sentence_tokens"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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LJ+yIPf69eunmTNn6urVq8y4W4RcunQpVxNfXb16VampqS5IlL+eeeYZ7d27V506ddI777yj8PBwzjECCqlWrVqpVatWdsfIExnX9DVNU8OGDVP79u316KOPZrutl5eXAgMD1bp16yyXCUHBt3btWv3jH/9QiRIlNGLECA0ePFjVqlWTYRiKiYnRZ599pg8++ECjR49WgwYN1KVLF1vzMiIJlypMn7Ig965fv65u3bqpePHimjFjhmrVqmV3JOSBqlWrqmLFitq9e7dzWXYjkqGhofLw8NBPP/1kR8w8U6NGDUk3Licg3bg2W0BAgPNyAr9lGIaOHTvm0nwAsrpw4YJbTaLTqVMn3XPPPRo7dqzdUZAP7rnnHq1bt06RkZE5zsy7detWdezYUV27dtWqVatcnDAziiRcZu3atbr77rtv+SnLtWvX9M0339j+KQtyr3PnzkpJSdHWrVvl4eGhkJAQBQUF5fgGfN26dTakhFURERH67LPP9O2336pTp06SshbJpUuXqm/fvho5cqTeeecdO+PetuxerzfjcDjyKQmA3CpWrJjuvPNOhYWFKSwsTOHh4W5VLFG0lC9fXk2bNtXatWtvul3Xrl21Z88eXbhwwUXJskeRhMsUtk9ZkHtW3oAbhqH09PR8TIO88tNPP6lJkyby8vLSq6++qgceeECBgYGKiIjQ66+/rqVLl2rMmDFKTU3VgQMHVL16dbsjA3AzzZs31759+zJd7qJ+/frOUhkWFlakznm+du2a4uLiVL58+UynksTHx2vq1Kk6ePCgqlatqqefflo1a9a0MSn+CF9fXz3wwAP673//e9PtBgwYoKVLlyopKclFybJHkYTLFLZPWZB7GYcC5hazYxYeX375pQYPHqxr165lu97Hx0fz589Xz549XZwMQE769OmjypUr6z//+Y/dUVwiMTFRGzduVGRkpNavX6/9+/dnKpZ169ZVeHi4wsPD9eCDD9qc9vZMmDBB06ZN044dO9SsWTNJUnJysho2bKijR486TxuqUKGC9u/fr8qVK9sZFxaFhoYqNTVVR44cUbFi2U9lk5aWptq1a6t48eI6fPiwixNmZu04HuA2XLt2LVefClasWDHHN60omEJCQizdCrvOnTtr2rRpt9zutddeU+fOnV2QKP/07t1bBw8e1JNPPqm6devKx8dHXl5eqlGjhv7617/qwIEDlMhCqkaNGho3btwtt5swYQIjG4XMqlWr3OrD2NKlS+v+++/Xa6+9pt27d+vixYtatmyZ/vGPf6hJkyaKjo7WBx98oP79+9sd9bZ99913qlmzprNEStLcuXN15MgRderUSWvWrNGoUaMUFxen6dOn25gUf0SvXr0UGxurYcOGZTsTemJioh577DGdPHlSvXv3dnm+32PWVrhMcHCwtm7dqrS0tJt+yrJ169ZczRQJ2CUyMlLVqlW75XbR0dHasGFD/gfKZyEhIXrzzTftjuFSly9f1rFjx3T58uUcp9rv2LGji1PlrZiYGJ0/f/6W28XFxSkmJib/AyHPVK9eXVevXrU7hm28vLxUokQJlShRQr6+vvL09CwSM0tL0smTJ7Nc0mPZsmUyDEOzZs1ScHCwunbtqq+//lqrV6/O1YeeKDgmTJigJUuWaN68efrqq6/UvXt35/uN2NhYff3110pMTFSNGjU0YcIEe8OKIgkX6tWrl15//XUNGzZMb7/9tsqUKZNpfWJiov7+97/r5MmTevrpp+0JiTxx6dKlm74Br1q1qosT2eP69es5fmiCgungwYN66qmnFBkZectrtbnLub5Xr15V8eLF7Y4BC/r376/XXntN586dU0BAgN1x8t3169e1ZcsWRUZGKjIyUjt37lRKSopM01RISIgGDBjgPLS1sIuPj8/0/sk0TW3atEkNGzbM9CF8o0aNtGbNGhsS4naUK1dOUVFR+utf/6qVK1dme33u++67TzNmzFDZsmVtSJgZ73DgMoXtU5a8kpaWpgsXLig5OTnHbYpCsTp37pz++c9/atmyZTc9pMowDKWlpbkwmT0SExO1ZcuWInN+iju8jo8cOaL27dsrMTFR7dq109mzZ3XixAn169dPx48f1549e5SWlqaePXtm+SCsKHI4HIqOjtb69esL/c/W3UyYMEHbt29XWFiYXn31Vd1///1F9sOAjh07ZimO/fr1cxbHonA6xW8FBAToxIkTzvu7d+9WfHy8Bg8enGk7roFbeAUGBmr58uU6ceKENm3apDNnzjiXt2/fvkBNbMdkO3CpM2fOOD9lyU7GpyyBgYEuTpb3vv32W7388svatm3bTQ+pKQrF6uzZs2rRooXOnDmjKlWqKDU1Vb/++qvatGmj48eP65dffpFhGGrTpo2KFy+u9evX2x3ZsoxrDEo3Dgks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fc3q1as7/85mJzU11axevbrZp08fFybLe+72e6pRo0Zm+fLlzStXrpimaZoRERFZDk/euHGj6enpac6cOdOOiLhNhem9cu4vEgbkAcMwNGjQIMXFxWn16tWSbgzVe3h4FImT/TNcu3YtV9fmio+Pd0Ga/FehQgVVqFDB7hgu4y77626v47Nnz6pp06Y33cYwDDVt2lRnz551Uar8ExAQoJIlS9odA/kgMjJSrVu3lqenZ47bFCtWTK1bt9aGDRtcmCzvudvvqaNHj6ply5bOf7sZk7z99nqRHTp0ULt27fTee+/ZkhG3pzC9V6ZIwuUiIiJkmqbmzJmjX375Rd9++606deqkoKAgu6PlmSpVquiHH3646TamaergwYOqXr26i1Llnz//+c/auHGjrl+/bncUl3CX/XW313Hp0qV16tSpW2536tQplS5d2gWJ8le/fv0UGRmpq1ev2h0FeSxjVsdbOXfuXKH/PeZuv6c8PT3l7+/vvJ9RKM+fP59puypVqig6Otql2ZB3Cst7ZYokXK5OnTpq0aKFVqxYoffee0/p6ekF68ThPNC9e3dFR0drwYIFOW7z8ccf69SpU7rvvvtcmCx/vPzyy6pWrZp69uypY8eO2R0n37nL/rrb67hNmzbavHmzVq5cmeM2q1at0ubNm9W2bVsXJssfkyZNUr169dSzZ08dPXrU7jgFwrBhw/TXv/5VW7ZssTvKbWnYsKGioqL07bff5rjNunXrtHHjRjVs2NCFyfKeu/2eqlKlin7++Wfn/Vq1akmStm3blmm7AwcOFMlJ4NxFYXmvzHUkYYv33ntPTzzxhIoVKyZfX1+dO3dOvr6+dsfKMz///LMaNmyoK1eu6B//+IceeOABtW3bVg8++KDGjx+vpUuXatq0afL399f333+vO+64w+7It6Vz585KSUnR1q1b5eHhoWrVquV4XUXDMLRu3TobUuYdd9lfd3sdb926VR07dpRhGHr44Yc1YMCATDNdzp8/XwsWLJDD4VBUVJRat25tb2CLOnfunGXZb1/HISEhCgoKKnKvYys8PDycs5927dpVL774YpYZQAuDZcuWqXfv3vLy8tKAAQP08MMPKyQkRNKN1/KiRYs0b948paamaunSperZs6fNif84d/s9NWzYMC1dulTnzp2Tt7e3jh49qjp16ig4OFgffPCBqlSpog8//FDv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bceIJAAAAADAEookAAAAAMASiiQAAAAAwBKKJAAAAADAEookAAAAAMASiiQAAAAAwBKKJAAAAADAEookAAAAAMASiiQAAAAAwBKKJAAAAADAEookAAAAAMASiiQAAAAAwBKKJAAAAADAEookAAAAAMASiiQAAAAAwBKKJAAAAADAEookAAAAAMASiiQAAAAAwBKKJAAAAADAEookAAAAAMASiiQAAAAAwBKKJAAAAADAEorkTRiGoWrVqll6TEREhAzDUGRkZKbl1apVk2EYeRcOAAAAAGxCkQQAAAAAWFLM7gDuYt26dUpNTbU7BgAAAADcNoqki9SsWdPuCAAAAACQJwr8oa3Xr1+Xj49Ptucq9u7dW4ZhqH379lnWNW/eXB4eHjp//rxz2cWLFzVhwgTVr19fvr6+8vf3V+fOnbVixQrLuT755BM1btxYvr6+CggIUEREhM6dO5fj9tmdIxkTEyPDMBQeHq6kpCSNHz9eISEh8vb2Vq1atTR16lSZppnt823YsEGdO3eWn5+fypYtq3vvvVe7du3S7NmzZRiGJk2aZHmfAAAAACA3CnyR9PHxUatWrRQbG6uYmBjncofDoY0bN0qSdu7cqWvXrjnXJSQkaO/evapfv74qVqwoSTp8+LAaN26sV199VUlJSbr77rvVvHlzbd++XT169NBrr72W60zjx4/Xo48+qh9//FEdO3ZUx44dtXr1arVq1UoXL160vI8pKSnq1q2bPvroIzVv3lydOnXS6dOnNX78eD3//PNZtl+yZIm6dOmi9evXq0GDBurevbtOnjyp9u3ba/v27Za/PgAAAABYUeCLpCSFh4dLUqaZUPfv36/4+HjdeeedSklJ0ZYtW5zrNm7cKIfD4Xxcenq6/vznP+vUqVOaNm2ajh07pi+//FLr1q3T/v37Vb16dY0fP14HDx68ZZZt27Zp2rRp8vf31/bt27VmzRotWrRIx44dU61atbR8+XLL+7d161Z5enrqxIkT+uKLL/T1118rKipKnp6emj59uq5cueLcNjExUY899pjS09M1b948bd26VfPnz9fBgwc1duxYffDBB7f8esnJyUpMTMx0AwAAAIDcKrRFMuP/J06cmOO6sLAwSdLy5cv1/fffq2/fvnrmmWfk4fG/3a5Vq5Zef/11paen66OPPrpllvfff1+maervf/+7mjRp4lxe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If3888+SpKtXr2rs2LGqVq2afHx81KBBAy1evDjHvIcOHVJERISCg4Pl7e2tSpUqqV+/fvrhhx8ybRceHq6hQ4dKkl544QUZhuG8zZ49O8vzXrx4UY8//rgqV64sb29vNWjQQJ988km2GbI7R/K351taeS5JWrJkiVq3bq0SJUqoQoUKevDBB3X06NFM30MAAAAAyC+WRiR3796t9u3b6/r162rYsKF69eqla9eu6fjx43rrrbfUu3dvVa5cWZI0ZcoUPfvssypWrJjCwsJUoUIFbd68WVOnTtXSpUu1ceNGVapUKcvX2L59u0aMGKEGDRro7rvv1p49e/T5559r//792rFjh7p27arY2Fh17NhRcXFx2rBhgx566CGtXr1ad999d6bn+vLLL9WvXz8lJyercePGat26tU6dOqVFixZp+fLlWr16tTp27ChJ6t69u9LS0rR582Y1atRIjRs3dj5PrVq1Mj3vpUuX1KZNG125ckUdOnRQXFycNm7cqEcffVQOh0PDhw/P9ffU6nO99dZbeuqpp+Th4aGOHTsqICBA27dvV8uWLdWjR49cf10AAAAA+KMsFcm3335b169f12uvvaann34607qffvpJ/v7+kqSdO3fqn//8p0qVKqVvv/1WrVq1kiQlJydr8ODB+vzzzzVy5MhsRxI/+OADvf/++xoxYoQkKTU1Vffee6++/fZbtW3bVgEBATp+/LhKliwpSZo5c6aGDx+uyZMnZyqSMTExGjRokIoXL64VK1borrvucq77+uuv1bNnTw0aNEhHjx6Vl5eXxo8fr4CAAG3evFm9e/e+6YQ7X331lfr166fZs2fL29tb0o3S+sADD+ill16yVCStPNfx48c1duxYeXl56euvv1anTp0kSWlpafrLX/6iWbNm5eprJicnKzk52Xk/MTEx13kBAAAAwNKhrefPn5ekTKUsQ926dZ2jke+++64cDoeefPJJZ4mUJG9vb7377rvy9fXV0qVLderUqSzP0759e2eJlKTixYvrySeflHSjrL7//vvOEilJERERqlChgrZu3arU1FTn8jfffFNXr17VlClTsuTt3r27Hn/8cZ06dUorV6608i2QJJUuXVrvvvuus/hJUu/evdWgQQOdPHlSMTEx+fJcn3zyiVJSUjR48GBniZSkYsWK6Y033lCpUqVy9TWnTJkif39/5y04ODjXeQEAAADAUpFs1qyZJGnkyJGKjIxUWlpatttFRUVJkgYOHJhl3R133KFu3brJ4XBo8+bNWdZ369Yty7IaNWpIkqpVq6bQ0NBM6zw9PRUSEqLU1FTFxcU5l3/zzTeSpD59+mSbsUOHDpKkHTt2ZLv+Zpo1a6by5ctnWZ6R7ffniubVc2V8vx588MEs25cpUybb7112JkyYoISEBOctu0IPAAAAADmxdGjrM888o02bNikyMlKdOnVSqVKl1KZNG913332KiIhwHtp65swZScoywUyGjOWnT5/Osq5KlSpZlmWMtGW37rfrf3u4ZsZIXk6PyfDb8plbQUFB2S738/PLkiMvnyujVOY0gli1atVcfU1vb+9MI6AAAAAAYIWlIlm6dGl999132rx5s5YvX67IyEh99913Wrt2raZMmaKoqCjVrl37ls9jGEaO6zw8ch4kvdm633M4HJKkRx555Kbb/fbQ29yyksOVzwUAAAAArmD5OpKGYah9+/Zq3769JOnXX3/VU089pfnz5+u5557TokWLFBgYqBMnTig2Nlb169fP8hy5HS28HUFBQTp27Jhef/31bA8dLYwqV66s6OhonTp1KtvvK4eoAgAAAHCF2x4Ou+OOO5wznB48eFDS/84/nD9/fpbtz58/rzVr1sgwDLVr1+52v3yOunbtKklaunRprh/j5eUlSTme+2m3jO/XF198kWVdQkKC87xQAAAAAMhPlorkBx98oBMnTmRZvmrVKkn/O3dv5MiR8vDw0Ntvv61du3Y5t0tJSdGTTz6ppKQk9enTJ19nC3366afl6+urMWPGaMmSJVnWJycna/Hixfr555+dywIDAyVJ0dHR+ZbrdgwdOlReXl6aM2eONm7c6Fyenp6up59+WpcvX7YxHQAAAAB3YenQ1g8++ECPP/646tevr3r16qlYsWL66aeftH//fvn4+GjixImSpJYtW+qll17Sc889pzZt2ig8PFwVKlTQ5s2bderUKdWuXVv/+c9/8mWHMtSqVUvz58/XgAED1LdvX9WqVUv16tVTyZIldfr0ae3Zs0dXr17V3r17nRPetG7dWnfccYcWL16s8PBw1ahRQx4eHho2bJjatm2br3lzo2bNmpo2bZqeeuopderUSWFhYapUqZJ27NihixcvatCgQZo7d65zZBUAAAAA8oOlEcmXXnpJw4YNk2EY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nnpqjj322Nx8881f+TOpAABA9arqILlu3bq0tLRkxIgRmTZtWh566KGMGDEiCxYsyNtvv5329vYeQfL222/Pww8/nNNPPz0bNmzI888/n0cffTR//etfc/nll+ell17KTTfd1GMfV155ZTZu3JgZM2Zkw4YNWbx4cZ599tm0t7dnwIABmTdvXv7yl7+Ux9977735+OOPc9VVV+XNN9/MkiVLsmzZsqxduzabNm3KpEmTemz/2muvTUdHR1asWJEZM2aks7MzN954Y4488shMmTIlbW1t2b59e6Hj0t3dna6urh4TAADA/qq6ILlly5b86le/yre//e2MHj06t956a7q7uzN37tysWbMm69aty09+8pMcccQRPdb77LPP8vOf/zwDBw7M4sWL09TUVF524IEHZuHChWlqasqiRYuye/fuJMnGjRvzxBNPZNCgQVm4cGEOPPDA8joTJ07MlVdemV27duWuu+4qz//ggw+SJFOnTu1V+4gRIzJy5Mhe8+vr6zNt2rQ8+OCDef/99/Pb3/42p59+ep5//vnMmjUrTU1Nueyyy9Le3p5SqbTPY9Ta2prGxsby1NzcvM91AAAA9qiqIPmjH/0ohx12WObOnZtXX30106dPz/Lly7N58+bceeedGTdu3Oeuu3bt2nz44YeZMGFCDj300F7L+/fvn5NPPjlbtmzJhg0bkiR/+tOfkiRnnXVWhg4d2mudmTNnJklefPHF8ryTTz45SfLjH/84TzzxROEH6Rx88MGZPXt2nnvuubzzzju5+eabM3z48Nx3330544wzctRRR/W67/PftbS0ZOvWreWpo6OjUA0AAEBtq6oguWbNmnR3d6e+vj7z5s3LXXfdlfPOOy/f+MY39rnupk2bkiTPPPNM6urq9jo9+eSTSZIPP/wwSbJ58+YkyZFHHrnXbe6Z39nZWZ43e/bsXHjhhXnjjTfyve99L0OGDMlpp52WW265Je+//36hfpubm9PS0pK77747p5xySrmPfd0/2dDQkMGDB/eYAAAA9tcBlS7gP6m1tTWLFi3KkiVLsmDBgrS2tmbq1KmZOXNmzj///AwcOPBz191zuerRRx+dU0899Qv3c8ghh+xXPXV1db3m1dfX55FHHskNN9yQxx57LM8991zWrFmTF198MbfeemtWrFiRCRMm7HPb69atS1tbWx544IFycDzuuONy6aWXZvLkyftVHwAAwP9FVQXJ8ePHZ/z48bnzzjuzdOnS3H///Vm5cmWefvrpDBo0KBdccEFmzpyZKVOmpF+/nidjhw8fniQZNWpU7rvvvv3a37Bhw5L86+s89mbPWc7DDz+817KxY8dm7NixmT9/frq6ujJ//vzccccdueaaa/LSSy/tdXt/+9vfsnjx4rS1teWVV15J8q9Qe9VVV+XSSy8tn5UEAAD4MlXVpa17DBw4MLNmzcqzzz6bTZs25Wc/+1kOO+ywtLW15cwzz0xzc3PmzZuX1157rbzOt771rTQ2NuaFF17Ixx9/vF/7mThxYpJkxYoV+fvf/95r+QMPPJAkvZ7E+u8GDx6c1tbW1NXV5fXXX++xbMeOHXnkkUfy3e9+N4cffniuvvrqvPrqqzn33HOzdOnSbN68OQsXLhQiAQCAPlOVQfJ/GzFiRG688ca8+eabWbVqVa644op88sknue2223LCCSeU73tsaGjID3/4w2zbti3Tp0/Pxo0be22rs7MzbW1t5ddHHXVUzjnnnGzbti1XX311/vnPf5aXrV69Or/+9a9TX1+fuXPnlue3tbX1CotJ8tRTT6VUKvV6guq5556biy++OE8++WTGjBmTX/ziF+ns7Mxjjz2W6dOn93hSLAAAQF+oqktb92XChAmZMGFCfvnLX2b58uW5//77e3xdxg033FC+93D06NEZO3ZsvvnNb2bnzp1Zv3593njjjRx//PHlp7EmyT333JNJkybld7/7XV544YWMHz8+H3zwQdrb27Nr167cfvvtOfHEE8vjly5dmlmzZmXkyJEZM2ZM+vfvn7fffjtr1qxJv379smDBgh41H3LIIbnuuusye/bsjBkz5ks/RgAAAPtSV9qfLx6sMY8//ngWLVqUl19+OVu2bMmQIUPS3NycKVOm5KKLLspJJ53UY/xHH32U1tbWLF++PB0dHRkwYEDGjRuX66+/PmeeeWaPsX/84x/z+9//PqtWrUpHR0e2b9+eYcOGlcdX4hLVrq6uNDY2Jqnb6wOCqlFtve1rqdekvr52/j9WW+/j2rPnIXC1oH//QZUuoU/Nue6/K11Cn1q5bEmlS+gzE6eeU+kS+tTyxfdUuoQ+849/bKl0CX2mVCplx45t2bp16xd+u4MgiSBZ9WqpV0GS6iFIVi9BsnoJktVLkOyt6u+RBAAA4D9LkAQAAKAQQRIAAIBCBEkAAAAKESQBAAAoRJAEAACgEEESAACAQgRJAAAAChEkAQAAKESQBAAAoBBBEgAAgEIESQAAAAoRJAEAAChEkAQAAKAQQRIAAIBCBEkAAAAKESQBAAAoRJAEAACgEEESAACAQg6odAFUXqlU2vNTyj9WvZpptOaUaudNXFO91qba+f3W2nu5u/vTSpfQp3bt+qzSJfSZnTX2u929e3elS+gztfQ5tafXffVcV6qlo8Jevffee2lubq50GQAAwFdER0dHhg8f/rnLBUmye/fubN68OQcffHDq6ur6bL9dXV1pbm5OR0dHBg8e3Gf7rYRa6jXRbzWrpV6T2uq3lnpN9FvNaqnXRL/VrFK9lkqlbNu2LcOGDUu/fp9/J6RLW0m/fv2+8L8NX7bBgwdX/QfBHrXUa6LfalZLvSa11W8t9Zrot5rVUq+JfqtZJXptbGzc5xgP2wEAAKAQQRIAAIBCBEkqpqGhIT/96U/T0NBQ6VK+dLXUa6LfalZLvSa11W8t9Zrot5rVUq+JfqvZV71XD9sBAACgEGckAQAAKESQBAAAoBBBEgAAgEIESQAAAAoRJAEAAChEkAQAAKAQQRIAAIBCBEkAAAAK+R/P4ZHLnBJOhAAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_attention(sentence_tokens, translation, attention)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "sentence = \"Ein Mann sieht sich einen Film an.\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "translation, sentence_tokens, attention = translate_sentence(\n",
    "    sentence,\n",
    "    model,\n",
    "    en_nlp,\n",
    "    de_nlp,\n",
    "    en_vocab,\n",
    "    de_vocab,\n",
    "    lower,\n",
    "    sos_token,\n",
    "    eos_token,\n",
    "    device,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['<sos>', 'a', 'man', 'watches', 'a', 'a', 'a', '.', '<eos>']"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "translation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA3sAAALWCAYAAAD/OIpnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAABTIElEQVR4nO3deZyd86E/8M+ZkEU2YkuzSAQRpbGrrbJYaimCe9uKilBVbRVNVeV2w7UUJXp7L7W1lqotSlJLq1QItTQl/OwhCWkoQmQiicky5/eHm7mmWWSmkjPnyfv9ep1Xnec8Z+Yz386cnM/zfZ7vKZXL5XIAAAAolJpKBwAAAOCTp+wBAAAUkLIHAABQQMoeAABAASl7AAAABaTsAQAAFJCyBwAAUEDKHgAAQAEpewAAAAWk7AEAABSQsgcAAFBAyh4AAEABKXsAAAAFpOwBAAAUkLIHAADwTyZOnJixY8dm1qxZlY7SbMoeAADAPzn00ENzyCGH5Kqrrqp0lGZT9viXFOGIBwAAfNQDDzyQqVOnplwu5+qrr650nGZT9viXFOGIBwAAfNQ111yTJNlhhx3y7LPP5oknnqhwouZR9mi2ohzxAACAxebOnZvRo0dniy22yM9+9rOUy+Vce+21lY7VLMoezVaUIx4AALDY7373u7z//vs58sgjs8cee6Rnz5757W9/m4ULF1Y6WpMpezRLkY54AADAYldffXVqampy5JFHJkm+8pWv5J133smdd95Z4WRNp+zRLEU64gEAAEkybdq03H///Rk4cGC6d++eJBk2bFjK5XLDWW3VRNmjWYp0xAMAAJLkuuuuS/JhwVts8803z/bbb5+77ror77zzTqWiNYuyR5MV7YgHAAAkH65JsdZaa+Wwww5rtP3II4/M/Pnzc8MNN1QoWfMoezRZ0Y54AADAI488kkmTJmXIkCFp3759o8cOP/zwrLHGGlU3sbFGpQNQfZZ3xOPkk0/ODTfckBNOOKFC6SiaKVOmZPz48XnjjTdSV1e31H1KpVJ+9KMfreJkAECRXHPNNSmVSo0mNBZbf/31s88+++Tuu+/Os88+my233LICCZuuVC6Xy5UOQfV45JFHsttuu+WII45omOFb7O2330737t2z9dZb569//WuFElIU8+fPz7HHHpvrr78+SbK8l6pSqZRFixatqmjA/1q4cGHeeeedZR6ISZKNNtpoFSYCaJ66urp07do17du3z7Rp01IqlZbY56abbsrhhx+eU045Jeeff34FUjadmT2apIhHPGiZfvzjH+c3v/lN1l577XzlK19J375907Fjx0rHApLce++9Oeuss/Loo49mwYIFy9yvVCpZpRmoCk8++WS22WabHHzwwUstekly8MEHZ88998zf//73VZyu+czsscKKesSDlmmjjTbK+++/nyeffDK9evWqdBzgf91xxx055JBDsmjRoqyzzjrZeOONl3sg5v7771+F6QD4KDN7rLCiHvGgZXrrrbfy+c9/XtGDFuaMM85IfX19Ro0alRNOOCGtWrWqdCQAlsHMHtAibb755unXr1/GjBlT6SjAR6y11lrZbrvt8tBDD1U6CgAfw0cvAC3SMccck3HjxuXtt9+udBTgIzp06GDRFaCQJk2alGuvvTZTpkxptP3RRx/NzjvvnA4dOuTTn/50fve731UoYdMpe0CL9L3vfS/77bdfBg0alPvvv3+5q3HyoWuvvTZ/+ctfPna/Rx99NNdee+0qSEQR7bXXXpkwYUKlYwB84i688MIcc8wxWXPNNRu2vfnmm/n85z+fxx9/PPPmzcsLL7yQL33pS3niiScqmHTFKXs0SRGPeNAybbrppnn88cfz/PPPZ6+99kq7du3Su3fv9OnTZ4nbJptsUum4LcLw4cNz5ZVXfux+V111VY4++uhVkIgiOu+881JbW5vvf//7VtoECuWhhx7KNttskx49ejRs+9WvfpXZs2dnxIgRmTdvXn73u9+lvr4+F110UQWTrjgLtNAkF154Ya688spMnTq1YdviIx6zZ89OqVRqOOLx2GOPZbvttqtcWKraR3/Hkg8/d++1116rTJiCqa+vX+YiS/Bxfv3rX2e//fbLz372s9x6660ZOHBgevTokZqaJY8fl0ql/OhHP6pASoCme+ONNzJw4MBG2/7whz+kTZs2Of3009O6desMGTIkn/3sZ/PYY49VJmQTKXs0yccd8TjnnHNy11135bDDDstFF12U3/zmNxVMSzWrr6+vdITCmjx5cjp16lTpGFSp008/PaVSKeVyOZMnT87kyZOXua+yB1STDz74oNEKw3V1dfnrX/+az372s+nQoUPD9o033jhPPfVUJSI2mbJHkxTxiAdUszPPPLPR/YkTJy6xbbGFCxfmxRdfzIMPPpi99957VcSjgH79619XOgLAStGjR488/fTTDffvvffefPDBBxk8eHCj/ebNm5f27duv6njNouzRJEU84gHV7KOzLKVSKRMnTszEiROX+5wNNtgg55xzzqoJSOEcddRRlY4AsFIMHjw4l19+eU4++eTsueeeGTlyZEqlUg4++OBG+/2///f/0rNnzwqlbBpljyYp4hEPWra5c+dmwoQJeeONN1JXV7fM/YYNG7YKU7Uci2dZyuVyjjnmmOy+++756le/utR9W7dunW7dumXnnXdOmzZtVmVMAGjxRo4cmZtvvjm/+MUv8otf/CLlcjlf+tKXsvXWWzfs8+yzz+aVV17JCSecUMGkK07Zo0mKeMSDluvHP/5xRo0alblz5y5zn8UzWqtr2fvoLMs111yT/fbbz8wLq8TChQtz55135vHHH8+MGTPy2c9+Nsccc0yS5PXXX8+MGTPy6U9/Omus4a0GUB022mijPPXUU7nyyivz9ttvZ/vtt8/w4cMb7fPkk0/m4IMPzhe/+MXKhGyiUtmHV9EEr732Wrbddtu89957SdJwxOOGG25o2OfZZ5/NZz7zmZxwwgn5r//6rwolpdqdf/75Oe2009KqVavst99+6du3bzp27LjM/X/yk5+swnSwenvooYfyla98JdOmTWs44HLUUUflV7/6VZLk1ltvzRe/+MXccsstOfTQQyucFmD15XAbTVLEIx60TFdccUXatWuX8ePH+wgPaEGee+657LvvvlmwYEG+/e1vZ/fdd1/i9f7AAw/MWmutlVtvvVXZA6ggM3tAi9S2bdsMHjw4d911V6WjVJXnnnsu559/fh588MG88cYbmT9//lL3K5VKPhCbZjn88MNzyy235K677so+++yTJKmpqcnw4cMbZvaSZMCAAXn77bfz3HPPVSoqQLM8/fTT+Z//+Z+MHz8+06dPT5J07949e+yxR775zW+mf//+FU644szsAS1S165dLfLTRI888kj22muvzJs3L0nSpUuXdO3atcKpqsO4ceMaCvKyFgIqlUq56qqrVnGyluf+++/PTjvt1FD0lqV79+5WZQaqzs9//vN873vfy6JFi/LRObEXXnghL7zwQn71q1/lggsuyEknnVTBlCtO2aNZinTEg5bpy1/+cq666qrMmTNH6VtBI0eOzLx583LyySfnhz/8Ybp06VLpSC3erFmzcvDBB2f8+PH5uBNdlL0Pvffeeyu0ANecOXOyYMGCVZCoejzwwAN54IEHHFSAFupPf/pTvvOd72SttdbK8ccfnyOPPDK9e/dOqVTK1KlTc9111+WXv/xlRowYka222ip77rlnpSN/LKdx0mTLOuKx2BprrFFVRzxomT744IPss88+WXPNNXPZZZdl0003rXSkFq9Dhw7p27dvnnjiiUpHqRrHH398Lr/88my66aY5/vjjP3YhoAEDBqzCdC3TRhttlPXXXz9/+9vfGrYt7TTOvn37pqamJi+88EIlYrYo7777bg455JA89NBDK3RQYdGiRasoGfBR++23X+67776MGzcuu+6661L3eeSRR7LHHntk7733ropLTczs0SRFPOJBy7T//vunvr4+48aNyxZbbJFevXqlR48eqampWWLfUqmU++67rwIpW5bWrVunX79+lY5RVcaMGZMNN9wwjz76qJnQFTR48OBcd911uf/++zNo0KCl7nPbbbfl5Zdfzre+9a1VnK5l+s53vpPx48dnyy23zHHHHZc+ffqkQ4cOlY4F/JPHH388AwYMWGbRS5JddtklAwcOzGOPPbYKkzWfmT2apIhHPGiZllbqlsWR8A8ddNBBee211zJx4sRKR6kaa621Vvbff/+MHj260lGqxgsvvJBtt902rVu3zk9/+tMccsgh6datW4YPH54LL7wwt912W0455ZQsWLAgTz/9dDbeeONKR664Ll26pEOHDnnuueeUPGjB2rVrl0MOOSS//e1vl7vf0KFDc9tttzVcI9+SKXs0ybrrrpvtttsuf/rTn5a73957750nnngi77zzzipKRtG8+uqrTdq/V69eKylJ9XjmmWey884757zzzjOjsoK23nrrdOvWLXfffXelo1SV22+/PUceeWTmzp271Mfbtm2bG264IQcddNAqTtYyde7cOfvuu29uuummSkcBlqNv375ZsGBBJk2alDXWWPoJkAsXLsxmm22WNddcMy+99NIqTth0TuOkSebOnZv111//Y/dbf/31l/kmAFaE8vbxrr322iW2HX300TnxxBNz8803Z++9917mqa9JMmzYsJUdscX79re/nW9/+9t5+eWXXRfaBEOGDMkzzzyTUaNG5U9/+lOmTp2a+vr69OjRI3vvvXe++93vZpNNNql0zBZj++23zz/+8Y9KxwA+xsEHH5wLL7wwxxxzTP7rv/4ra6+9dqPHa2trc9JJJ+W1117Ld7/73cqEbCIzezRJEY94QLWqqalJqVRaYvtHX9aX9bhTX//P97///fzmN7/JWWed1VCQ4ZP05z//Ofvuu2/Gjh2bfffdt9JxgGV49913s+OOO2bq1Knp0KFD9t133/Tu3TvJh2cc/eEPf0htbW369OmTv/71r1lnnXUqG3gFmNmjSYp4xIPq8N5772X27NnLXMluo402WsWJKu/HP/7xUsscy9aqVaulbi+Xyzn22GOX+1wfRE9zDR48OL/97W8zbNiw7L///tl7773TvXv3Zc6677HHHqs4IZB8eH3t+PHj8/Wvfz133nlnbrnlliX2OeCAA3LZZZdVRdFLzOzRREU84rGqLFy4MO+8884yP1spWT0Ly/L84x//yA9/+MOMHTt2udd/ehPOilq8enBzTZky5RNMU/28rq24q6++Oj/4wQ9W6HROs+5QeVOmTMlDDz2U119/PUnSrVu37L777lW36JSyR5O9/vrrDUc8lmbxEY9u3bqt4mQt07333puzzjorjz766HI/YFhhaeyNN97IjjvumNdffz3du3fPggUL8tZbb2WXXXbJ5MmT8+abb6ZUKmWXXXbJmmuumfvvv7/SkWG14XWtaa6++up89atfTblczrbbbvuxH73w61//ehWmA4rMaZw0Wbdu3fL73/++MEc8VqY77rgjhxxySBYtWpR11lknG2+88XI/sJn/c9ZZZ+X111/PmWeemR/+8Ic5+uijc+211+bhhx9Okjz44IP5xje+kVKpZCXFpXjqqafy+OOPZ8aMGdlyyy0bVkWsq6tLXV1dOnXqVOGEVCuva013/vnnp02bNrnrrrsycODASscBVtCkSZMyY8aMrLvuuunbt2+l4zRPGVhpdthhh3JNTU354osvLi9cuLDScapKnz59yn369Gm4P3z48HJNTU2jff7+97+X27dvX/6P//iPVR2vxXrhhRfKu+yyS7mmpqbhdvTRRzc8/qtf/apcU1NTvvvuuyuYkmrmda3p2rVrV953330rHQNYAR988EF55MiR5XXXXXep/45ed9115W233bb85JNPVi5kE6z4pxbDx3j++eczevToPPbYY5WO0mI8++yz2WWXXXLSSSctc2EIlm769OnZZpttGu4vHr+PXhvUvXv3DBo0KDfffPOqjtciTZs2LXvssUceffTRHHjggTn//POXWNDmi1/8Ylq3bp1bb721QilbliuvvDJdunTJH/7wh2Xuc/fdd6dLly65+uqrV12wFszrWtN17949a621VqVjAB9j3rx5GThwYM4777y0bt06+++//xL/jg4ePDhPPfVU1bz3UPZokptuuimDBw9eotCdcsop2WqrrfKlL30pu+66a8MpPqu7Dh06WJygmf75NMPFK79Onz690fa2bdsusW11deaZZ2bGjBm58sorc/vtty91Rdz27dtnm222cVDmf914441p06ZN9tlnn2Xus88++6R169b57W9/uwqTtVxe15pu2LBhuf/++/Puu+9WOgqwHOeff34ee+yxHHPMMZk8eXJ+//vfL7FPt27d8ulPfzr33ntvBRI2nbJHk/zmN7/JxIkTs+222zZs+8tf/pKLLrooHTt2zJe//OX07t07Y8eOzfXXX1/BpC3DXnvtlQkTJlQ6RlXaaKON8tprrzXc32qrrZIkd911V8O2uXPn5uGHH86nPvWpVZ6vJfrDH/6Q/v3755hjjlnufr1791aQ/9dzzz2X/v37L3MJ/OTDWeWtt946zz333CpM1nJ5XWu6//iP/8igQYMyaNCgjBs3bpkfIcPyLVy4MG+++WZee+21Zd7gX3HTTTdlo402yqWXXpq2bdsuc7/NN98806ZNW4XJmk/Zo0meeeaZ9O/fP61bt27Ydt1116VUKuXmm2/O9ddfn7/+9a/p0KFDrrzyygombRnOO++81NbW5vvf/74V6Zpo8ODBefrpp/P2228nSQ466KC0b98+3/ve93LaaaflF7/4RQYNGpQ333wz++23X4XTtgxvvfVWNt9884/db8GCBZk7d+4qSNTyvfvuu1lvvfU+dr/11lsvM2bMWAWJWj6va0232Wab5cknn8wzzzyTPffcM+3atUvv3r3Tp0+fJW6bbLJJpeO2OPfee28GDhyYDh06pFu3btl4442XeuvTp0+lo1LlpkyZkh122CFrrLH8NSxbt26dmTNnrqJU/xqrcdIkb731VnbddddG2+6///5ssMEGDadBdenSJXvssUf+9re/VSJii/LrX/86++23X372s5/l1ltvzcCBA9OjR4+lziKUSqX86Ec/qkDKlumII47ItGnT8txzz2XAgAHp0qVLLrvsshx99NE5//zzUyqVUi6Xs+WWW+bss8+udNwWYd11112hI9svvfSS2dD/td5662XSpEkfu9+kSZN8buj/8rrWdFOnTm10f/78+WahVpDVX1mV2rVrt0IlbsqUKVXzb4KyR5O0a9cutbW1DfffeOONvPTSS/niF7/YaL+11167ao54rEynn356QymZPHlyJk+evMx9vSlqbOutt84NN9zQaNvhhx+e3XbbLXfddVdmzpyZvn375qCDDsqaa65ZoZQty2677Zbbb789EydObLS4zUc98MADeeaZZzJ8+PBVmq2l2n333XPLLbdk3Lhxy1wSf9y4cZkwYUIOPfTQVRuuhfK61nT19fWVjlC1zjjjjNTX12fUqFE54YQTLArESrXNNttkwoQJefvtt7P++usvdZ8pU6bkySefXO613i2JskeT9OnTJ+PHj897772XtddeO9dff31KpdISv/D/+Mc/ssEGG1QoZcvhg3E/eRtttFGOP/74SsdokU455ZTcdtttOfjgg/PLX/5yib/LP//5zxk+fHjWWGONnHzyyZUJ2cKMGDEio0ePzpAhQ/LDH/4wX/va19K5c+ckSW1tbS6//PKcffbZqampyXe+850Kp20ZvK6xKn109VdY2b72ta9l3LhxOfzww3PjjTcucZr/e++9l2OOOSYLFizIcccdV6GUTVMqu0qYJrjkkktywgknZOONN84222yTO++8M23atMnkyZOz7rrrJvnweqANNtggO+ywQ/70pz9VODHVburUqXnwwQfzxhtvNPrYhY8ye/B/Lrnkkpx00kmpr6/PWmutlblz56ZDhw6pqalJbW1tSqVSLrnkkqr5R2pV+PnPf54RI0Y03O/SpUuSNFo58YILLmi0D7BqbLDBBtlrr72shssqM3To0Nx4443p0KFDdt1119xzzz3ZbLPN0q9fvzzwwAOpra3NsGHDqubjeJQ9mmTBggUZOnRow2d0tW/fPldccUW+/OUvN+xz22235bDDDsvZZ5+dkSNHVioqVe6DDz7I1772tYZ/4Jf3UlUqlXzUx0c8+uij+elPf5o///nPef/995N8+BEVAwcOzA9+8IPstttuFU7Y8jz44IP56U9/mgceeCDz5s1L8uFp6wMHDsz3v//97LHHHhVOSDX5V6/H89EW/2fo0KGZMGFCXnrppUpHYTVRLpfzs5/9LBdccMESC3N17tw5p556ak477bSUSqUKJWwaZY9mmTp1at5+++3069dviQulJ06cmFdffTU777xzNtxwwwolpNqddNJJ+cUvfpENNtggRxxxRPr06ZMOHTosc/+jjjpqFaarDuVyOTNmzEh9fX3WW28917qsgEWLFuWdd95J8uHiLcv7SAZYlpqamma/ESyVSlY5/Yhp06Zlxx13zFFHHZWzzz77Y1dJhE/KokWL8sQTT2Tq1Kmpr69Pjx49suOOOzZakb4aKHvwCRo8eHBKpVKuueaa9OjRI4MHD17h55ZKpdx3330rMV116dq1a+rr6/P000+na9eulY4Dqy2va003cODAf+mo//333/8JpqluZ555ZqZMmZJrr702G2+8sdVfoYmUPVbYtGnT8uSTT6Zfv37p27fvMve7++67Uy6Xs//++6/CdC3D4qO5zz//fPr27dukWQGnIjbWoUOH7Lvvvhk9enSlo8BqzesalbT4929F3q76feNfUdT3uebCWWELFizIkCFDMmjQoGUeqf1//+//5YADDsh+++1XNX8En6QpU6YkSbp3797oPk231VZbNfqYD5ZkxqXpjFnTeV2jkqz+yqpS1Pe5yh4rrE+fPtltt93ywAMPZNq0aenZs+cS+1x33XUplUqr7fVTvXr1Wu59Vtx3v/vdHHHEEXnyySez7bbbVjpOizRu3LiUSqXMnTu34f6KqpYLyz9pxqzpvK5RSavr+wlWvaK+z1X2aJKjjjoqDz/8cK6//vqcdtppjR4rl8v57W9/m7XXXjtDhgypTMAW6p133slvfvObPP7445kxY0b23HPPnHrqqUk+/AyhV155JXvttVfWWmutCidtOf793/89f//737P33nvnhBNOyN57753u3bsv8xSy1XH1OjMuTWfMPjle15btwQcfTJLstNNOadu2bcP9FWX1V6iMIr7Pdc0eTTJ79ux07do1vXr1ynPPPdfosXvvvTf77LNPvv71r+fSSy+tUMKW55Zbbsmxxx6b999/P+VyueGI0K9+9askyT333JP99tsv11xzTb7yla9UOG3Lct999+Ub3/hGXnnlleXuZ/U6WLW8ri3f0q5zbMrMsOvOGiuXy7n++uszZsyYTJo0KbNnz17qNXylUulj/72A5Sni+1wzezRJx44dM2TIkNx4442ZMGFCdthhh4bHqnFqe2V75JFHMnTo0HTq1CkXXnhhdt999+y0006N9tlzzz3TuXPn/O53v1st3xQtyx133JFDDz00CxcuzHrrrZdevXot96MXSN588828+OKL2XzzzRt97Mkrr7ySH/zgB3nmmWey0UYb5cc//nF23nnnCiZtOYxZ03ld+3iL/x3s3LlzkmTYsGGr7WnA/6r58+fngAMOyJ///OdlLtKyogu4wMcp5PvcMjTRH//4x3KpVCqfeOKJDdvmzJlT7tixY3nzzTevYLKW5wtf+EK5devW5b/97W8N20qlUvnoo49utN+ee+5Z3myzzVZ1vBZtu+22K6+xxhrlq6++ulxfX1/pOFXh5JNPLtfU1JRffPHFhm2zZs0qd+3atVxTU1MulUrlUqlUXmuttcovvfRSBZO2HMas6byufbyBAweWzzvvvIb7DzzwQKPfMVbcOeecUy6VSuWDDjqo/PLLL5eHDRtWrqmpKc+fP7/8wgsvlM8444xyx44dy6eeemqlo1IQRXuf69NiabLF107deOONDaea3H777Xn//fczbNiwCqdrWf7yl79kl112yXbbbbfc/bp27Zo33nhjFaWqDs8//3z22GOPHHXUUY6Ir6Bx48bl05/+dKMlo6+++uq8+eabOfzww/Piiy/moosuyrx583LhhRdWMGnLYcyazuvax3vggQfywgsvNNwfNGhQzjvvvAomql433XRTunTpkt/+9rfZZJNNGq7bXnPNNbP55pvnxz/+ce68885ceOGFDacRw7+iaO9zlT2arFQq5Stf+UpmzJiRu+++O8mHU9s1NTVV+UewMs2dOzfrr7/+x+43c+bMVZCmuqy33npZb731Kh2jqkyfPj19+vRptO3OO+/MGmuskYsvvjibbbZZTj755Gy99dZ54IEHKpSyZTFmTed17eO1bt06c+bMabhfLpedZthML7/8cnbaaae0b98+SRrK3keva/zc5z6X3XbbLZdccklFMlIsRXufq+zRLMOHD0+5XM61116bN998M/fee28GDRqUHj16VDpai9K9e/c8++yzy92nXC7nmWeeycYbb7yKUlWHf/u3f8uDDz6YDz74oNJRqsbs2bMbrXy4aNGiPPLII9l+++0bFed+/frl73//eyUitjjGrOm8rn28TTfdNPfdd18eeOCBvPbaa0mS999/P6+99toK3fg/rVq1arj2MUlD6Xv77bcb7de9e/e8+OKLqzQbxVWk97nKHs2y+eabZ8cdd8wdd9yRSy65JIsWLaq+C1ZXgX333TcvvvhibrzxxmXuc+WVV2batGk54IADVmGylu+ss85K7969c9BBB1ldbQV169at0aljDz30UN5///0MHDiw0X4LFy5M69atV3G6lsmYNZ3XtY933HHH5d13383gwYMbCu+tt96ajTfe+GNv/zzTvLrr3r17owMtm266aZLk0UcfbbTf008/bREvPjFFep9rNU6a7aijjsoJJ5yQc889Nx07dsxhhx1W6UgtzmmnnZbf/va3GTZsWJ588skccsghSZI5c+bkySefzG233Zbzzz8/66+/fr7zne9UOG3L8oUvfCGtWrXKfffdl379+qV3797L/Jy9UqmU++67rwIpW5ZddtklN9xwQy6++OLsueee+eEPf5hSqZQDDzyw0X7PP/98w+fMre6MWdN5Xft4J554Ynr06JExY8bk73//e+6///5ssMEG6devX6WjVZ2dd945t912W+rq6tKmTZvsv//++c53vpOTTz45bdu2Tffu3XP55Zfn+eefX+LvluUbM2ZMZs2alSRVeXriylaY97mVXB2G6jZz5sxy27ZtyzU1NeVjjjmm0nFarL/85S/lT33qU+VSqVSuqalpdCuVSuUNN9yw/Oijj1Y6ZouzeBXEFbnV1NRUOm6L8Mwzz5TbtWvX6Pdr8ODBjfaZMmVKuVQqlY899tgKpWxZjFnzeF1rmqWtVsqKueOOO8pdu3Ytjx07tmHbiBEjGv3ulUqlcocOHax42kT9+vVrGEOWVJT3uWb2aLa11147J554Yh5//PEcd9xxlY7TYu2yyy558cUXc9VVV+VPf/pTpk6dmvr6+vTo0SN77713vv71rze6HoEPTZkypdIRqs6WW26Zhx56KD//+c8zY8aMbL/99vne977XaJ8//vGP2XrrrTNkyJDKhGxhjFnzeF1rmp/85CfZdtttKx2jKh1wwAFLrOp64YUXZscdd8ztt9+emTNnpm/fvjnxxBOz2WabVShlddpxxx3TtWvXSsdosYryPrdULlseCgAAoGgs0AIAAFBAyh4AAEABKXsAAAAFpOzxL6mrq8vpp5+eurq6SkepGsaseYxb0xmz5jFuTWfMmse4NZ0xax7j1nRFGTMLtPAvqa2tTefOnTNr1qx06tSp0nGqgjFrHuPWdMaseYxb0xmz5jFuTWfMmse4NV1R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AApI2QMAACggZQ8AAKCAlD0AAIACUvYAAAAKSNkDAAAoIGUPAACggJQ9AACAAlL2AAAACkjZAwAAKKBSuVwuVzoEy1dbW5vOnTv/771SRbNUk5oaxzKao3XrdpWOUHXmzZtd6QhVqUuXT1U6QtWZNevtSkeoSq1arVHpCFVn4cIFlY5Qlbytbg5j1lyzZs1Kp06dlruPd8MAAAAFpOwBAAAUkLIHAABQQMoeAABAASl7AAAABaTsAQAAFJCyBwAAUEDKHgAAQAEpewAAAAWk7AEAABSQsgcAAFBAyh4AAEABKXsAAAAFpOwBAAAUkLIHAABQQMoeAABAASl7AAAABaTsAQAAFJCyBwAAUEDKHgAAQAEpewAAAAWk7AEAABSQsgcAAFBAyh4AAEABKXsAAAAFpOwBAAAUkLIHAABQQMoeAABAASl7AAAABaTsAQAAFJCyBwAAUECrZdkbPnx4SqVSxo0bV+koAAAAK0VVlL1x48alVCpl+PDhlY4CAABQFaqi7AEAANA0yh4AAEABNansffDBB2nbtm169+69xGNDhgxJqVTK7rvvvsRjO+ywQ2pqavL2228nScaPH58TTjgh/fv3zzrrrJN27dqlX79+Oe200/Lee+81eu7w4cMzaNCgJMk111yTUqnUcDv99NMb7Ttt2rSceOKJ6du3b9q1a5cuXbpkhx12yBlnnJHa2tql/kwPPvhgBg8enI4dO6ZTp0454IAD8txzzy1zDP7whz/kgAMOyPrrr582bdqkT58+GTFiRN55550l9p0/f34uueSS7Ljjjll33XWz1lprpXfv3vnCF76QG2+8cZnfAwAA4F9VKpfL5aY8YcCAAXnwwQczZcqUhtJXX1+f9dZbLzNnzkzr1q0zc+bMrLXWWkmSWbNmpUuXLtliiy3yzDPPJEl23nnnPPXUU+nfv3969uyZDz74IE888UTeeOONbLnllnn00UfToUOHJMmVV16Z0aNH549//GM22WSTRmVyyJAhGTJkSJIPC+RBBx2U9957L717986OO+6YefPm5YUXXsjLL7+cJ598Mttss02SDwvkNddckxEjRuTnP/95dthhh/Tq1SsTJ07MSy+9lHXXXTfPPPNMunbt2uhnP+2003LeeeeldevW2XHHHfOpT30qTz31VCZNmpRNNtkkDz/8cDbccMOG/f/93/89o0ePTseOHfO5z30unTp1yvTp0/P0009nm222WeEFYmpra9O5c+f/vVdqwv9bq7eaGhPXzdG6dbtKR6g68+bNrnSEqtSly6cqHaHqzJr1dqUjVKVWrdaodISqs3DhgkpHqEpNfFtNksSYNdesWbPSqVOn5e7T5Fe/gQMH5sEHH8y4ceMaFkx56qmnMnPmzGy55ZZ59tln85e//CV77bVXkg9nzurr6zNw4MCGr/GTn/wku+6660cKTFJXV5cTTzwxl19+eS666KL8+Mc/TpIce+yx2XTTTfPHP/4xu+++e66++uolMr377rs57LDD8t577+WCCy7IiBEjGr3Rf+SRR9KtW7clnnfxxRfn1ltvbSiMixYtype+9KXceuutueSSS3LmmWc27HvLLbfkvPPOy1ZbbZXbbrstm266aZIP/6hPP/30nHnmmTnppJMaZuymTJmS0aNHp1evXvnb3/6Wddddt+FrffDBB3nyySebMOoAAABN0+Spj8Wl7aOzUov/e3FBW9pjAwYMaNi23377NSp6SdKmTZtcfPHFWWONNTJmzJgmZbryyivz9ttvZ999980pp5yyxIzOLrvskg022GCJ5x1++OENRS9JWrVqlZEjRyb5sKR+1Nlnn50kueGGGxqKXpKG00m32WabjB49OjNmzEiShlNWt91220ZFL0natm2bXXbZZZk/T11dXWpraxvdAAAAmqLJM3s777xz2rRps0Sh69ixYw477LD06tVrqWXvozN7STJ9+vT8/ve/zwsvvJDa2trU19cnSVq3bp1JkyY1KdO9996bJPn617/epOfts88+S2zr27dvkuSNN95o2PbWW2/lqaeeymabbZatttpqieeUSqXstttumThxYv72t7/l85//fPr165f27dvnzjvvzAUXXJAjjjhiqbOLS3PuuefmjDPOaNLPAgAA8FFNLnvt2rXLTjvtlPHjx2fq1KnZaKONMn78+Hzuc59Lq1atMnDgwNxwww2ZO3duFixYkIkTJ+bTn/501l9//YavcdFFF+W0007LggWfzPng06ZNS5JssskmTXpejx49ltjWsWPHJB/Ori02derUJMmkSZNSKi3/mrnFM3udOnXKFVdckeOOOy6nnnpqTj311PTt2zeDBg3KkUcemd12222ZX2PkyJEZMWJEw/3a2tr07NlzhX8uAACAZl2xPHDgwIwfPz7jxo3L1ltvnZkzZzbM3A0cODDXXHNN/vKXv2TevHmpr69vdArno48+mu9+97vp3Llzfv7zn2fgwIHp2rVr2rRpkyTp1q1bo1m1lWlFF/BYPOvYtWvXfP7zn1/uvr169Wr478MPPzx77bVXxowZk3vuuScPPPBALrvsslx22WUZMWJELrzwwqV+jTZt2jSMBwAAQHM0q+wNGDAg//mf/5lx48Zl5syZSdKo7CUfnr45b968RtuS5Lbbbkvy4TVwRx11VKOvO2/evPzjH/9ocp6ePXvmhRdeyCuvvJLPfOYzTX7+x1k8A7jeeustdYGY5Vl//fVz7LHH5thjj025XM4f//jHfOlLX8pFF12UY445JltuueUnnhcAAKBZa9Pvuuuuad26dcaNG5dx48alU6dO2W677ZIkvXv3brhub2mLsywuh0s7hfKWW25Z6pK1rVu3TpIsXLhwqXkWr/x5+eWXN+fH+Vg9evRIv3798txzz+Wll15q9tcplUrZd999c8ABByRJnn322U8qIgAAQCPNKnuLr9t79dVXc8899zRcr7fYwIED8/jjj2fixInp169fo8+eW7wAylVXXdXomr3nnnsu3//+95f6/RYvbPLiiy8u9fFjjz026623Xu6+++5cfPHFSxTGRx99NG+99VZzftQGP/rRj1JfX5/DDjssEydOXOLxd955J1dccUXD/SeffDK/+93vMn/+/Eb7vfvuu3nssceSxHV4AADAStPsTxkdMGBAHnrooXzwwQdLrLS5+Lq9xf/9UUcffXQuvPDC/P73v8/mm2+eHXfcMe+++24eeOCBDBkyJI8//nheffXVRs/p3bt3+vfvnwkTJmSnnXbKlltumVatWuWggw7KQQcdlC5duuSWW27JQQcdlO985zv5r//6r4YPVX/++ecbPlR9aR+/sKKGDh2aZ599Nuecc0623377bLPNNtlkk01SLpfzyiuv5Omnn06HDh3yta99LUny6quv5rDDDkvnzp2zww47pGvXrnnvvffy4IMPZvbs2TnwwAOX+/ELAAAA/4pmzewljUvc0sresh5bd91189e//jVDhw7N/PnzM3bs2EyfPj3/+Z//mRtuuGGZ32/xh59Pnjw51157ba666qo88cQTjb7PU089leOPPz7lcjm33357Hn744XTu3Dlnnnlmk1fqXJqzzz47DzzwQA477LD84x//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urq6hvu1tbUVTAMAAFQjM3st0LnnnpvOnTs33Hr27FnpSAAAQJVR9lqgkSNHZtasWQ23adOmVToSAABQZZzG2QK1adMmbdq0qXQMAACgipnZAwAAKCBlDwAAoICUvVVs2LBh6devX2677bZKRwEAAApM2VvFXnvttbz44ouZNWtWpaMAAAAFpuwBAAAUkNU4V7Fx48ZVOgIAALAaMLMHAABQQMoeAABAASl7AAAABaTsAQAAFJCyBwAAUEDKHgAAQAEpewAAAAWk7AEAABSQsgcAAFBAyh4AAEABKXsAAAAFpOwBAAAUkLIHAABQQMoeAABAASl7AAAABaTsAQAAFJCyBwAAUEDKHgAAQAEpewAAAAWk7AEAABSQsgcAAFBAyh4AAEABKXsAAAAFpOwBAAAUkLIHAABQQMoeAABAASl7AAAABaTsAQAAFJCyBwAAUEBrVDoATVWqdIAqUq50AFYTNTWtKh2hKrVt077SEapOqeR3rTlmvjmz0hGqzgcfzKl0hKrUpnW7SkeoPmXv15qqXK7PByu4r5k9AACAAlL2AAAACkjZAwAAKCBlDwAAoICUPQAAgAJS9gAAAApI2QMAACggZQ8AAKCAlD0AAIACUvYAAAAKSNkDAAAoIGUPAACggJQ9AACAAlL2AAAACkjZAwAAKCBlDwAAoICUPQAAgAJS9gAAAApI2QMAACggZQ8AAKCAlD0AAIACUvYAAAAKSNkDAAAoIGUPAACggJQ9AACAAlL2AAAACkjZAwAAKCBlDwAAoICUPQAAgAJS9gAAAApI2QMAACggZQ8AAKCAlD0AAIACatFl75VXXslrr71W6RjL9cgjj+SDDz6odAwAAIBGWlzZq62tzZVXXpnPfe5z2XTTTfPEE080erxcLueGG27I4MGDs84666Rt27bZYostcvrpp2fu3LlL/ZrvvPNOvve972WzzTZL27Zt06VLl+y777655557lrr/q6++mm984xvp27dv1lprrXTp0iVbbrllvv71r+fFF19stO/IkSPTtWvXfP3rX89f/vKXT2YQAAAA/kUtouzV19fnnnvuyRFHHJGuXbvma1/7Wh5++OEMGDAg/fr1a7TfEUcckaFDh+avf/1rttlmm+y///6ZM2dOzjjjjAwaNCjz5s1r9LWnT5+enXbaKT/72c8yf/78DBkyJNtuu23uvffefP7zn8+oUaMa7T9t2rRst912+eUvf5kk2X///TNgwIC0adMmV1xxRR555JFG+x988MFZa621cvnll2e33XZL3759c/bZZ7f4GUkAAKDYSuVyuVypb/7CCy/kmmuuyXXXXZfp06cnSTbffPMceeSR+cpXvpJevXo12v+CCy7IqaeemoEDB+aGG25I165dkyTz58/PN7/5zVx11VX5/ve/n5/+9KcNzznwwANzxx13ZOjQofn1r3+d1q1bJ0keeuihfP7zn09dXV0mTJiQbbbZJknyk5/8JGeeeWZOOOGE/OIXv2j0/V977bUsWLAgm2yySaPtixYtyr333ptrr702t99+e+bOnZtSqZRBgwZl+PDhOfTQQ9O+fftmj1NtbW06d+78v/dKzf46q5+K/WqzmunYsUulI1Sltm2a/7q4umrT1pg1x8C9/q3SEarOjH+8WekIVelvE/5Q6QhVp65u6WfmsWzlcn1mvz8zs2bNSqdOnZa77yovezNnzswNN9yQa665Jo8//niSZL311suXvvSlDBs2LDvttNNSn7dw4cJ86lOfyrx58/LKK69kww03bPT4vHnz0qdPn9TV1WXGjBmpqanJ5MmTs8kmm6RDhw559dVX06VL4zdk3/3ud3PRRRfl2GOPzRVXXJEk+eY3v5lLL700t99+ew4++OAm/3yzZ8/OrbfemmuvvTbjxo1LuVxOhw4d8m//9m856qijMmDAgJRKyy9sdXV1qaura7hfW1ubnj17/u89ZW/FKXusGspe8yh7TafsNY+y13TKXvMoe02n7DVdU8reKj2N8/vf/34+9alP5Vvf+laeeuqpHHroobn99tvz+uuv57//+7+XWfSS5IknnsiMGTOy6667LlH0kqRdu3bZfvvtM3PmzEyaNCnJh7N3SbLvvvsuUfSS5Mgjj0ySjB8/vmHb9ttvnyT5j//4j9xxxx1NXnylY8eOGT58eP785z/n1Vdfzdlnn50ePXrk6quvzqBBg9KnT58lrkP8Z+eee246d+7ccPu/ogcAALBiVmnZe+yxx1JXV5dWrVrle9/7Xv7nf/4nBx98cNZcc82Pfe7UqVOTJH/6059SKpWWervzzjuTJDNmzEiSvP7660mS3r17L/VrLt6++BTSJBk+fHi++MUv5rnnnsuBBx6YddZZJ3vssUfOOeec/OMf/2jSz9uzZ8+MHDkyv/zlL7PDDjs0/Bwfdz3fyJEjM2vWrIbbtGnTmvR9AQAA1liV3+zcc8/N5ZdfntGjR+ess87Kueeem7322itHHnlkhgwZstzr2urr65Mkm266aXbbbbflfp911113hfIs7XTKVq1a5aabbsppp52WMWPG5M9//nMee+yxjB8/Pj/96U/zhz/8IbvuuuvHfu0XXngh1113XX7zm980lLstt9yy4VTO5WnTpk3atGmzQj8DAADA0qzSsrfLLrtkl112yX//93/n1ltvzTXXXJN77rknf/zjH9OhQ4cccsghOfLII7PnnnumpqbxpGOPHj2SJP369cvVV1+9Qt+vW7duST78KIWlWTxb2L179yUe23bbbbPtttvm9NNPT21tbU4//fSMGjUqJ598csO1hv/srbfeyo033pjrrrsuEyZMSPJh8TzhhBNy1FFHNczuAQAArGwVXY0z+XCFy2uvvTbXXnttw7V23bp1y9ChQ3PkkUemf//+ST5ctGTDDTdMfX19pk6dutRr8P7Z4gVaOnbsmNdeey1rr712o8e/973v5Wc/+1mjBVqWpa6uLu3atUvbtm0bfZ7fvHnzMnbs2Fx33XX54x//mIULF2bNNdfMfvvtl6OOOipf+MIXGlYAbS6rcTaXBVpYNSzQ0jwWaGk6C7Q0jwVams4CLc1jgZams0BL07XYBVqWZqONNsoPf/jDvPTSS3n44Ydz3HHHZe7cufnZz36WrbfeuuE6vDZt2uTUU0/N7Nmzc+ihh2by5MlLfK3p06fnuuuua7jfp0+fHHDAAZk9e3ZOOumkLFiwoOGxRx55JJdeemlatWqVb33rWw3br7vuujzzzDNLfO2777475XJ5icVSDjrooHz5y1/OnXfemc985jO5+OKLM3369IwZMyaHHnrov1z0AAAAmqPiM3tL88EHH+T222/PNddck29961v5whe+kOTD6/aGDx+e6667Lq1bt862226bjTfeOPPnz8+LL76Y5557Lv3798/EiRMbvtb06dPzuc99LlOmTEmvXr2yyy675O233864ceOyaNGiXHjhhRkxYkTD/kOGDMmYMWOyySab5DOf+UzatWuXKVOm5LHHHkupVMqNN96Yf//3f2/Y/8tf/nK6d++e4cOH5zOf+cxKGQ8ze83V4n61KSgze81jZq/pzOw1j5m9pjOz1zxm9prOzF7TtejP2fskjB07Npdffnn++te/ZubMmVlnnXXSs2fP7LnnnvnSl76U7bbbrtH+77zzTs4999zcfvvtmTZtWtZaa63stNNO+e53v5t99tmn0b4PPvhgbr755jz88MOZNm1a5syZk27dujXsX4nr7pS95qq6X22qlLLXPMpe0yl7zaPsNZ2y1zzKXtMpe01X+LK3ulH2msuvNquGstc8yl7TKXvNo+w1nbLXPMpe0yl7TVdV1+wBAADwyVP2AAAACkjZAwAAKCBlDwAAoICUPQAAgAJS9gAAAApI2QMAACggZQ8AAKCAlD0AAIACUvYAAAAKSNkDAAAoIGUPAACggJQ9AACAAlL2AAAACkjZAwAAKCBlDwAAoICUPQAAgAJS9gAAAApI2QMAACggZQ8AAKCAlD0AAIACUvYAAAAKSNkDAAAoIGUPAACggJQ9AACAAlL2AAAACkjZAwAAKCBlDwAAoICUPQAAgAJao9IB+Hjlcvmj9yqWA1i6xn+jrKj6+vpKR6g69fWLKh2hKs2fX1fpCFVn4cL5lY5QlbyuNV25bMyaavH7jhV5/1Eqe5fS4v39739Pz549Kx0DAABoIaZNm5YePXosdx9lrwrU19fn9ddfT8eOHVMqlSodp5Ha2tr07Nkz06ZNS6dOnSodpyoYs+Yxbk1nzJrHuDWdMWse49Z0xqx5jFvTteQxK5fLmT17drp165aamuVflec0zipQU1Pzsa290jp16tTi/hBaOmPWPMat6YxZ8xi3pjNmzWPcms6YNY9xa7qWOmadO3deof0s0AIAAFBAyh4AAEABKXv8S9q0aZOf/OQnadOmTaWjVA1j1jzGremMWfMYt6YzZs1j3JrOmDWPcWu6ooyZBVoAAAAKyMweAABAASl7AAAABaTsAQAAFJCyBwAAUEDKHgAAQAEpewAAAAWk7AEAABSQsgcAAFBA/x/nseDEj3ITeAAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_attention(sentence_tokens, translation, attention)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:08<00:00, 115.43it/s]\n"
     ]
    }
   ],
   "source": [
    "translations = [\n",
    "    translate_sentence(\n",
    "        example[\"de\"],\n",
    "        model,\n",
    "        en_nlp,\n",
    "        de_nlp,\n",
    "        en_vocab,\n",
    "        de_vocab,\n",
    "        lower,\n",
    "        sos_token,\n",
    "        eos_token,\n",
    "        device,\n",
    "    )[0]\n",
    "    for example in tqdm.tqdm(test_data)\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "bleu = evaluate.load(\"bleu\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "predictions = [\" \".join(translation[1:-1]) for translation in translations]\n",
    "\n",
    "references = [[example[\"en\"]] for example in test_data]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_tokenizer_fn(nlp, lower):\n",
    "    def tokenizer_fn(s):\n",
    "        tokens = [token.text for token in nlp.tokenizer(s)]\n",
    "        if lower:\n",
    "            tokens = [token.lower() for token in tokens]\n",
    "        return tokens\n",
    "\n",
    "    return tokenizer_fn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "tokenizer_fn = get_tokenizer_fn(en_nlp, lower)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "results = bleu.compute(\n",
    "    predictions=predictions, references=references, tokenizer=tokenizer_fn\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'bleu': 0.2830420566177357,\n",
       " 'precisions': [0.6118114963229776,\n",
       "  0.3586727243225702,\n",
       "  0.21587497792689386,\n",
       "  0.13548324617470464],\n",
       " 'brevity_penalty': 1.0,\n",
       " 'length_ratio': 1.0205238168172768,\n",
       " 'translation_length': 13326,\n",
       " 'reference_length': 13058}"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "results"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
